package yc.code.common.util;


import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Calendar;
import java.util.GregorianCalendar;

/**
 * @author <a href="Arthur@moneed.net">wqf</a>
 * @date Created in 2021/8/9 15:05 <br>
 */
public class InterestCalculationUtil {
    static int DECIMAL_SCALE = 9;
    static BigDecimal BIGDECAMAL_100 = new BigDecimal(100);
    static BigDecimal BIGDECAMAL_12 = new BigDecimal(12);
    static BigDecimal BIG_DECIMAL_30 = new BigDecimal(30);
    static BigDecimal BIGDECAMAL_360 = new BigDecimal(360);
    static final String FORMAT_DAY = "yyyy-MM-dd";

    /**
     * 等额本息 还款列表计算(自然月，即回款日期与出借日期相同)
     *
     * @param total    总金额
     * @param yearRate 年利率
     * @param sumTerm  总期数
     * @throws Throwable
     */
    public static void calculation_DEBX_ZRY(BigDecimal total, BigDecimal yearRate, int sumTerm) throws Throwable {
        BigDecimal monthRate = yearRate.divide(BIGDECAMAL_12, DECIMAL_SCALE, BigDecimal.ROUND_HALF_UP);

        //每月还款额=[总本金×月利率×(1+月利率)^还款月数]÷[(1+月利率)^总期数-1]
        BigDecimal tmp = monthRate.add(new BigDecimal(1)).pow(sumTerm);//(1+月利率)^还款月数
        BigDecimal monthPayTotal = total
                .multiply(monthRate)
                .multiply(tmp)
                .divide(tmp.subtract(new BigDecimal(1)), 2, BigDecimal.ROUND_HALF_UP);

		/*总利息
			计算总利息有3种方法:
			1.在循环中将每月应还利息累加
			2.总利息=月还本息×总期数-总本金
			3.公式：总利息=[总期数×贷款本金×月利率×(1+月利率)^还款月数]÷[(1+月利率)^还款月数-1]-总本金
			具体使用哪种根据情况而定，推荐第二种
		*/
        //2:
        BigDecimal sumInterest = monthPayTotal.multiply(new BigDecimal(sumTerm)).subtract(total);
        //3:
		/*BigDecimal sumInterest = new BigDecimal(sumTerm).multiply(total).multiply(monthRate)
				.multiply(BigDecimal.ONE.add(monthRate).pow(sumTerm))
				.divide(BigDecimal.ONE.add(monthRate).pow(sumTerm).subtract(BigDecimal.ONE),2,BigDecimal.ROUND_HALF_UP)
				.subtract(total);*/

        BigDecimal remainTotal = total;    //剩余本金付初始值为总本金
        BigDecimal monthPrincipal = BigDecimal.ZERO;
        Calendar calendar = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        System.out.println("等额本息--本金：" + total.setScale(2) + "\t年利率：" + yearRate.multiply(BIGDECAMAL_100) + "%\t总期数：" + sumTerm + "\t总利息:" + sumInterest);
        System.out.println("期次\t回款本息（元）\t回款利息（元）\t回款本金（元）\t剩余本金（元）\t回款日期");
        for (int i = 1; i <= sumTerm; i++) {
			/*
			 	每月应还本金和利息也有2种方法:
			 	1.公式:
			 		每月应还利息=总本金×月利率×〔(1+月利率)^还款月数-(1+月利率)^(还款月序号-1)〕÷〔(1+月利率)^还款月数-1〕
			 		每月应还本金=总本金×月利率×(1+月利率)^(还款月序号-1)÷〔(1+月利率)^还款月数-1〕
			 */
			/*BigDecimal interest = total.multiply(monthRate)
					.multiply(BigDecimal.ONE.add(monthRate).pow(sumTerm).subtract(BigDecimal.ONE.add(monthRate).pow(i-1)))
					.divide(BigDecimal.ONE.add(monthRate).pow(sumTerm).subtract(BigDecimal.ONE),2,BigDecimal.ROUND_HALF_UP);

			monthPrincipal = total.multiply(monthRate)
					.multiply(BigDecimal.ONE.add(monthRate).pow(i-1))
					.divide(BigDecimal.ONE.add(monthRate).pow(sumTerm).subtract(BigDecimal.ONE),2,BigDecimal.ROUND_HALF_UP);
			*/


            //2.月还利息=剩余本金×月利率
            //月还本金=月还本息-月还利息
            //具体使用哪种根据喜好而定，第二种更容易理解
            BigDecimal interest = remainTotal.multiply(monthRate).setScale(2, BigDecimal.ROUND_HALF_UP);
            //为避免因精度损失产生误差，最后一期  还款利息=月还本息-剩余本金
            //为避免利息产生负数的情况出现 当利息小于等于0时利息赋值为0.1元
            if (sumTerm == i) {
                interest = monthPayTotal.subtract(remainTotal);
                if (interest.compareTo(BigDecimal.ZERO) <= 0) {
                    interest = new BigDecimal(1).divide(new BigDecimal(10), 2, BigDecimal.ROUND_HALF_UP);
                }
            }
            //月还本金=月还本息-月还利息
            monthPrincipal = monthPayTotal.subtract(interest);

            remainTotal = remainTotal.subtract(monthPrincipal);
            calendar.add(Calendar.MONTH, 1);
            System.out.println(i + "\t" + monthPayTotal + "\t\t" + interest + "\t\t" + monthPrincipal + "\t\t" + remainTotal + "\t\t" + sdf.format(calendar.getTime()));
        }

    }

    /**
     * 等额等息 还款列表计算(自然月，即回款日期与出借日期相同)
     *
     * @param total
     * @param localDate
     * @param dayInterestRate
     * @param sumTerm
     */
    public static void calculationRepaymentPlan(BigDecimal total, LocalDate localDate, BigDecimal dayInterestRate, int sumTerm) {
        BigDecimal sumInterest = total.multiply(dayInterestRate).multiply(BIG_DECIMAL_30).multiply(new BigDecimal(sumTerm)).setScale(2, RoundingMode.HALF_UP);
        System.out.println("等额等息--本金：" + total.setScale(2) + "\t日利率：" + dayInterestRate.multiply(BIGDECAMAL_100) + "%\t总期数：" + sumTerm + "\t\t总利息:" + sumInterest);
        System.out.println("期次\t回款本息（元）\t回款利息（元）\t回款本金（元）\t剩余本金（元）\t回款日期");

        //每期应还本金，Principal=总本金/借款周期。保留2位小数。
        BigDecimal monthPayPrincipal = total.divide(new BigDecimal(sumTerm), 0, RoundingMode.DOWN);

        DateFormat dateFormat = new SimpleDateFormat(FORMAT_DAY);
        Calendar calendar = GregorianCalendar.from(localDate.atStartOfDay(ZoneId.systemDefault()));

        //剩余本金付初始值为总本金
        BigDecimal remainTotal = monthPayPrincipal.multiply(new BigDecimal(sumTerm));
        for (int i = 1; i <= sumTerm; i++) {
            calendar.add(Calendar.MONTH, 1);
            //每期应还利息，Interest=总借款额度*日利率*30。保留2位小数
            BigDecimal interest = total.multiply(dayInterestRate).multiply(BIG_DECIMAL_30).setScale(2, RoundingMode.HALF_UP);
            //剩余本金
            remainTotal = remainTotal.subtract(monthPayPrincipal);
            //为避免因精度损失产生误差，最后一期  还款利息=月还本息-剩余本金
            //为避免利息产生负数的情况出现 当利息小于等于0时利息赋值为0.1元
            if (sumTerm == i) {
                if (remainTotal.compareTo(BigDecimal.ZERO) <= 0) {
                    remainTotal = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
                }
            }
            System.out.println(i + "\t" + monthPayPrincipal.add(interest) + "\t\t" + interest + "\t\t" + monthPayPrincipal + "\t\t" + remainTotal + "\t\t" + dateFormat.format(calendar.getTime()));
        }

    }

    public static void main(String[] args) throws Throwable {
        //本金10000元，年利率15%，出借18个月
        BigDecimal total = new BigDecimal(5000);
        BigDecimal yearRate = BigDecimal.valueOf(0.72);
        int sumTerm = 3;
        calculation_DEBX_ZRY(total, yearRate, sumTerm);

        System.out.println("===============================================================\n");
        calculationRepaymentPlan(new BigDecimal(5000), LocalDate.of(2019, 9, 10), new BigDecimal("0.002"), 3);
    }
}
